﻿//#define PERMANENT
using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using alib.Collections.ReadOnly;
using alib.Enumerable;
using alib.Debugging;
using alib.Character;
using alib.Array;

namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// 
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract class Entry : Instance, IEntry
	{
		protected Entry(sync_obj_ctor_arg _) : base(_) { }
		protected Entry(Grammar g, Type t, ConsDefs consdef)
			: base(g.em, g, consdef.identifier.Text)
		{
			base.Initialize(consdef);

			this._id = (((int)consdef.tdl_type) << 24) | consdef.src_seq_id;

			if (t != null)
			{
				this.t = t;
				this.m_flags |= (t.m_flags & Flags.HasAppropriateFeatures |
											 Flags.HasLocalConstraints |
											 Flags.FeaturesBelow);
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public EntryMgr EntryMgr
		{
			[MethodImpl(MethodImplOptions.AggressiveInlining), DebuggerStepThrough]
			get { return Grammar.em; }
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		readonly Type t;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public sealed override Type InstanceType { [DebuggerStepThrough] get { return t; } }

		protected override Tfs EnsureExpanded()
		{
			//Debug.Assert(this._definition != null);
			//if (this._definition == InstanceType.Definition)
			//    return InstanceType.Expanded;
			Tfs exp;
			if (!InstanceType.HasAppropriateFeatures)
			{
				Debug.Print("{0} will use Definition for Expanded", Name);
				exp = this.Definition;
			}
			else
				exp = Unification.Expand(ftm.r_none, this, InstanceType.Expanded);

			//Console.WriteLine(exp.ToPathList(true));
			//exp.Name = "Expanded: " + Name;
			//Console.WriteLine("Expanded: " + Name);
			return exp;
		}

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// in fact only applicable to IChartLicense
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		BitArray _lex_rule_compat;
		public bool IsLexRuleCompat(LexicalRule rule)
		{
			BitArray ba = _lex_rule_compat;
			if (ba == null)
				ba = _lex_rule_compat = new BitArray(em._lexical_rules.Count << 1);
			bool f_no;
			int rix = rule.SubIndex << 1;
			if (ba[rix] == (f_no = ba[rix + 1]))
				if (fsu.UnifyCheck(Expanded, rule.DaughterNodes[0]))
					ba[rix] = true;
				else
					f_no = ba[rix + 1] = true;
			return !f_no;
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// 
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class StaticExpandEntry : Entry
	{
		protected StaticExpandEntry(sync_obj_ctor_arg _) : base(_) { }
		protected StaticExpandEntry(Grammar g, Type t, ConsDefs consdef)
			: base(g, t, consdef)
		{
		}
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Expand-on-demand support
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class DemandExpandEntry : Entry
	{
		protected DemandExpandEntry(Grammar g, Type t, ConsDefs consdef)
			: base(g, t, consdef)
		{
			//exp_tfs_id = Interlocked.Increment(ref SysObj.next_id);
		}

		/// an ID for the expanded TFS that will remain the same after weak resurrection, and is unique amongst all TFS ids.
		//protected readonly long exp_tfs_id;

#if PERMANENT
		MemoizedItem<Tfs> mtfs;

		public override Tfs EnsureExpanded(out bool f_did)
		{
			if (f_did = mtfs.FirstClaim())
			{
				/// call the expand helper and then set the permanent ID
				Tfs tfs = PreExpand().ToArrayTfs();
				tfs.id = exp_tfs_id;
				mtfs.SetResult(tfs);
			}
			return mtfs.Result;
		}
#elif FLEETING
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Hook the expansion of lexical entries and keep a weak reference. The published reference to a lexical entry or
		/// entries can be released but a weak reference will remain in the object for possible future recovery.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		Task<Tfs> _expand_task = null;

		public override Tfs EnsureExpanded(out bool f_did)
		{
			f_did = false;

			/// we have to count weak reference recoveries as unifications (and pay the penalty of re-installing
			/// them atomically) or else the regression test results for number of unifications won't exactly match
			Tfs _exp;
			if (wr != null && (_exp = (Tfs)wr.Target) != null)
				return _exp;

			/// if expansion is already underway, wait for the result and return it
			Task<Tfs> _et = _expand_task;
			if (_et != null)
				return _et.Result;

			/// prepare a closure of the work that would need to be done to expand this entry
			_et = new Task<Tfs>(_expand_func);

			/// install the work closure. if this manages to snatch a ref to someone else's task, you have your result
			Task<Tfs> _act = Interlocked.CompareExchange(ref _expand_task, _et, null);
			if (_act != null)
				return _act.Result;

			/// otherwise, only the interlocked race winner actually starts the task
			_et.Start();
			_exp = _et.Result;

			/// install the expanded TFS into the weak reference, initializing or reusing the weakref as the case may be
			WeakReference _wra = wr;
			if (_wra == null)
				_wra = Interlocked.CompareExchange(ref wr, new WeakReference(_exp, false), null);
			if (_wra != null)
				_wra.Target = _exp;

			/// now, only after installing into the weak ref, release the task (since it has a strong ref)
			_expand_task = null;
			f_did = true;

			/// note: do not install _exp into base._expanded
			return _exp;
		}

		Tfs _expand_func()
		{
			/// call the expand helper and then set the permanent ID
			Tfs tfs = PreExpand().ToArrayTfs();
			tfs.id = exp_tfs_id;
			return tfs;
		}
#else
		WeakReference wr = null;
#if DEBUG
		int c_resurrect = 0;
#endif

		Task<Tfs> _expand_task = null;

		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		/// <summary>
		/// Hook the expansion of lexical entries and keep a weak reference. The published reference to a lexical entry or
		/// entries can be released but a weak reference will remain in the object for possible future recovery.
		/// </summary>
		///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		protected override Tfs EnsureExpanded()
		{
			/// we have to count weak reference recoveries as unifications (and pay the penalty of re-installing
			/// them atomically) or else the regression test results for number of unifications won't exactly match
			Tfs _exp;
			if (wr != null && (_exp = (Tfs)wr.Target) != null)
				return _exp;

			/// if expansion is already underway, wait for the result and return it
			var _et = _expand_task;
			if (_et != null)
				return _et.Result;

			/// prepare a closure of the work that would need to be done to expand this entry
			_et = new Task<Tfs>(_expand_func);

			/// install the work closure. if this manages to snatch a ref to someone else's task, you have your result
			var _act = Interlocked.CompareExchange(ref _expand_task, _et, null);
			if (_act != null)
				return _act.Result;

			/// otherwise, only the interlocked race winner actually starts the task
			_et.Start();
			_exp = _et.Result;

			/// install the expanded TFS into the weak reference, initializing or reusing the weakref as the case may be
			WeakReference _wra = wr;
			if (_wra == null)
				_wra = Interlocked.CompareExchange(ref wr, new WeakReference(_exp, false), null);
			if (_wra != null)
				_wra.Target = _exp;

			/// now, only after installing into the weak ref, release the task (since it has a strong ref)
			_expand_task = null;

			/// note: do not install _exp into base._expanded
			return _exp;
		}

		Tfs _expand_func()
		{
			/// call the expand helper and then set the permanent ID
			Tfs tfs = base.EnsureExpanded();
			//tfs.Id = exp_tfs_id;
#if DEBUG
			c_resurrect++;
			if (c_resurrect == 6)
			{
				//Debug.WriteLine("warning: {0} resurrected more than 5 times.", Name);
			}
#endif
			return tfs;
		}
#endif
	};
}
